package com.ihr360.payroll.service.payslip.result.impl;

import cn.irenshi.entity.payslip.AppStaffPayslipDetailEntity;
import cn.irenshi.entity.payslip.AppStaffPayslipEntity;
import com.google.common.collect.Lists;
import com.ihr360.authority.sharing.annonation.AccessControl;
import com.ihr360.authority.sharing.service.AuthUserResourceService;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.meta.table.DynamicCell;
import com.ihr360.excel.ExcelUtil;
import com.ihr360.excel.entity.ExportParams;
import com.ihr360.payroll.dao.PayslipResultRepository;
import com.ihr360.payroll.dao.SalaryPlanRepository;
import com.ihr360.payroll.dao.SalaryPlanStaffRepository;
import com.ihr360.payroll.dao.StaffPayslipRepository;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.payslip.PayslipResult;
import com.ihr360.payroll.model.dto.salary.payslip.StaffPayslip;
import com.ihr360.payroll.model.dto.salary.payslip.embeddable.HeadCell;
import com.ihr360.payroll.model.dto.salary.payslip.embeddable.PayslipHeadSnapshot;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlanStaff;
import com.ihr360.payroll.model.dto.salary.plan.config.PayslipConfig;
import com.ihr360.payroll.model.dto.salary.report.HistoryPayPeriod;
import com.ihr360.payroll.model.dto.salary.task.period.ActivePayPeriod;
import com.ihr360.payroll.model.dto.salary.task.period.PayPeriodTerm;
import com.ihr360.payroll.model.dto.salary.task.period.StaffPayPeriodTermSalary;
import com.ihr360.payroll.service.payperiod.StaffPayPeriodTermSalaryService;
import com.ihr360.payroll.service.payslip.PayslipConfigService;
import com.ihr360.payroll.service.payslip.result.PayslipResultService;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author tomtao
 */
@Service
@AccessControl
public class PayslipResultServiceImpl extends AbstractSimpleRestServiceImpl<PayslipResult, Long> implements PayslipResultService {

    @Autowired
    private PayslipResultRepository payslipResultRepository;
    @Autowired
    private StaffPayslipRepository staffPayslipRepository;
    @Autowired
    private SalaryPlanRepository salaryPlanRepository;
    @Autowired
    private SalaryPlanStaffRepository salaryPlanStaffRepository;
    @Autowired
    private StaffPayPeriodTermSalaryService staffPayPeriodTermSalaryService;
    @Autowired
    private PayslipConfigService payslipConfigService;
    @Autowired
    private AuthUserResourceService authUserResourceService;

    /**
     * 实发合计项目 code
     */
    protected final static String TOTAL_TAKE_HOME_PAY = "totaltakehomepay";

    @Override
    public SimpleRestReposity<PayslipResult, Long> getSimpleRestReposity() {
        return payslipResultRepository;
    }

    @Override
    public List<PayslipResult> generatePayslipResult(HistoryPayPeriod historyPayPeriod, ActivePayPeriod activePayPeriod) {
        String companyId = historyPayPeriod.getCompanyId();
        List<Long> termIds = activePayPeriod.getTerms().stream().map(PayPeriodTerm::getId).collect(Collectors.toList());
        List<StaffPayPeriodTermSalary> staffPayPeriodTermSalaryList = staffPayPeriodTermSalaryService.getStaffSalaryByTerms(termIds);
        if (CollectionUtils.isEmpty(staffPayPeriodTermSalaryList)) {
            return Collections.emptyList();
        }

        SalaryPlan salaryPlan = activePayPeriod.getSalaryPlan();
        List<SalaryPlanStaff> salaryPlanStaffList = salaryPlanStaffRepository.findBySalaryPlanId(salaryPlan.getId());
        PayslipConfig payslipConfig = getDefaultPayslipConfig(salaryPlan.getId());
        Long defaultPayslipId = payslipConfig.getId();
        Map<String, Long> payslipIdAndStaffIdMap = new HashMap<>();
        salaryPlanStaffList.forEach(salaryPlanStaff ->
                payslipIdAndStaffIdMap.put(salaryPlanStaff.getStaffId(), salaryPlanStaff.getPaySlipConfigId()));

        Map<Long, List<StaffPayslip>> payslipStaffSalaryMap = new HashMap<>();
        staffPayPeriodTermSalaryList.forEach(staffSalary -> {
            Long payslipId = payslipIdAndStaffIdMap.get(staffSalary.getStaffId()) != null ?
                    payslipIdAndStaffIdMap.get(staffSalary.getStaffId()) : defaultPayslipId;

            List<StaffPayslip> staffSalaryList = payslipStaffSalaryMap.get(payslipId);
            if (CollectionUtils.isEmpty(staffSalaryList)) {
                staffSalaryList = new ArrayList<>();
            }
            StaffPayslip staffPayslip = new StaffPayslip();
            staffPayslip.setStaffSalary(staffSalary);
            staffPayslip.setCompanyId(companyId);
            staffSalaryList.add(staffPayslip);
            payslipStaffSalaryMap.put(payslipId, staffSalaryList);
        });

        List<PayslipResult> payslipResults = new ArrayList<>();
        payslipStaffSalaryMap.forEach((payslipId, staffPayslipList) -> {
            PayslipResult payslipResult = new PayslipResult();
            payslipResult.setPayslip(new PayslipConfig(payslipId));
            payslipResult.setPayPeriod(historyPayPeriod);
            payslipResult.setCompanyId(companyId);
            payslipResult.setStaffPayslipList(staffPayslipList);
            payslipResult.getStaffPayslipList().forEach(staffPayslip -> staffPayslip.setPayslipResult(payslipResult));
            payslipResults.add(payslipResult);
        });

        payslipResultRepository.save(payslipResults);
        List<String> payslipResultId = Lists.newArrayList();
        payslipResults.forEach(payslipResult -> payslipResultId.add(payslipResult.getId().toString()));
        authUserResourceService.copyResourceAuthToNewResource(activePayPeriod.getSalaryPlan().getId().toString(), SalaryPlan.class.getSimpleName(),
                payslipResultId, PayslipResult.class.getSimpleName());

        return payslipResults;
    }

    private PayslipConfig getDefaultPayslipConfig(Long salaryPlanId) {
        SalaryPlan salaryPlan = salaryPlanRepository.findOne(salaryPlanId);
        if (salaryPlan == null || CollectionUtils.isEmpty(salaryPlan.getPayslipConfigs())) {
            return new PayslipConfig();
        }
        for (PayslipConfig payslipConfig : salaryPlan.getPayslipConfigs()) {
            if (payslipConfig.isDefaultPayslipConfig()) {
                return payslipConfig;
            }
        }
        return salaryPlan.getPayslipConfigs().get(0);
    }

    @Override
    public Map<String, String> getPayslipHead(Long id) {
        PayslipResult payslipResult = super.getOne(id);
        ValidationUtils.rejectIfEmpty(payslipResult, "", "没有找到对应的数据");
        return getPayslipHead(payslipResult);
    }

    private Map<String, String> getPayslipHead(PayslipResult payslipResult) {
        Map<String, String> headMap = new LinkedHashMap<>();
        if (payslipResult.getStatus() == PayslipResult.PayslipStatus.DEFAULT) {
            PayslipConfig payslipConfig = payslipResult.getPayslip();
            headMap = payslipConfig.getFields().stream().collect(Collectors.toMap(SalaryField::getCode, SalaryField::getName));
        } else if (payslipResult.getStatus() == PayslipResult.PayslipStatus.PUBLISH) {
            headMap = payslipResult.getHeadMap();
        }
        return headMap;
    }

    @Override
    public List<AppStaffPayslipEntity> getStaffPayslipList(String companyId, String staffId, int orderBy) {
        //TODO 测试
        //companyId = "7bb8c073-4ac7-40cd-a635-89b583c6df35";
        //staffId = "2bb16ff1-bd9b-484a-94f1-a249ba0983fb";
        List<StaffPayslip> staffPayslipList = staffPayslipRepository.findByCompanyIdAndStaffSalaryIdStaffIdAndStatus(companyId, staffId,
                PayslipResult.PayslipStatus.PUBLISH);
        if (CollectionUtils.isEmpty(staffPayslipList)) {
            return Collections.emptyList();
        }
        List<AppStaffPayslipEntity> result = new ArrayList<>();
        staffPayslipList.forEach(staffPayslip -> {
            StaffPayPeriodTermSalary staffPayPeriodTermSalary = staffPayslip.getStaffSalary();
            AppStaffPayslipEntity appStaffPayslip = new AppStaffPayslipEntity();
            appStaffPayslip.setYear(staffPayPeriodTermSalary.getYear());
            appStaffPayslip.setMonth(staffPayPeriodTermSalary.getMonth());
            appStaffPayslip.setPublishedAt(staffPayslip.getPublishedAt());
            appStaffPayslip.setId(staffPayslip.getId());
            result.add(appStaffPayslip);
        });
        return result;
    }

    @Override
    public AppStaffPayslipDetailEntity getStaffPayslipDetail(Long id) {
        StaffPayslip staffPayslip = staffPayslipRepository.findOne(id);
        Map<String, String> headMap = getPayslipHead(staffPayslip.getPayslipResult());
        StaffPayPeriodTermSalary salary = staffPayslip.getStaffSalary();
        if (salary == null) {
            return null;
        }
        AppStaffPayslipDetailEntity staffPayslipDetail = new AppStaffPayslipDetailEntity();
        staffPayslipDetail.setDepartmentName(salary.getDepartmentName());
        staffPayslipDetail.setStaffName(salary.getStaffName());
        Map<String, DynamicCell> data = salary.getSummarizedData().getData();
        Map<String, Object> salaryItem = new LinkedHashMap<>();
        headMap.forEach((code, name) -> {
            Object value = data.get(code) != null ? data.get(code).getCellValue() : null;
            if (TOTAL_TAKE_HOME_PAY.equals(code) && value != null) {
                staffPayslipDetail.setIncome(value.toString());
            } else {
                salaryItem.put(name, value);
            }
        });
        staffPayslipDetail.setSalaryItem(salaryItem);
        return staffPayslipDetail;
    }

    @Override
    public void exportPayslip(Long id, ServletOutputStream out) {
        ExportParams<List<Object>> exportParams = new ExportParams<>();
        PayslipResult payslipResult = super.getOne(id);

        Map<String, String> dynamicHeadMap = getPayslipHead(payslipResult);
        List<String> heads = new ArrayList<>(dynamicHeadMap.keySet());

        List<StaffPayslip> staffPayslipList = payslipResult.getStaffPayslipList();
        List<List<Object>> rowsDataList = new ArrayList<>();
        staffPayslipList.forEach(staffPayslip -> {
            List<Object> rowData = new ArrayList<>();
            Map<String, DynamicCell> data = staffPayslip.getStaffSalary().getSummarizedData().getData();
            rowData.add(staffPayslip.getStaffSalary().getStaffName());
            heads.forEach(headName -> rowData.add(data.get(headName) != null ? data.get(headName).getCellValue() : null));
            rowsDataList.add(rowData);
        });

        Map<String, String> headMap = new LinkedHashMap<>();
        headMap.put("staffName", "姓名");
        headMap.putAll(dynamicHeadMap);
        exportParams.setHeaderMap(headMap);
        exportParams.setRowDatas(rowsDataList);
        ExcelUtil.exportExcel(exportParams, out);
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public PayslipResult publishPayslip(Long id) {
        PayslipResult payslipResult = super.getOne(id);
        payslipResult.setStatus(PayslipResult.PayslipStatus.PUBLISH);
        Date nowDate = new Date();
        payslipResult.setPublishedAt(nowDate);
        PayslipHeadSnapshot payslipHeadSnapshot = new PayslipHeadSnapshot();
        List<HeadCell> headCellList = new ArrayList<>();
        payslipResult.getPayslip().getFields().forEach(salaryField -> headCellList.add(new HeadCell(salaryField.getCode(), salaryField.getName())));
        payslipHeadSnapshot.setHeadCellList(headCellList);
        payslipResult.setPayslipHeadSnapshot(payslipHeadSnapshot);
        payslipResult.getStaffPayslipList().forEach(staffPayslip -> {
            if (staffPayslip.getStatus() == PayslipResult.PayslipStatus.DEFAULT) {
                staffPayslip.setStatus(PayslipResult.PayslipStatus.PUBLISH);
                staffPayslip.setPublishedAt(nowDate);
            }
        });
        return super.createOrUpdate(payslipResult.getId(), payslipResult);
    }

    @Override
    public PayslipResult cancelPublishPayslip(Long id) {
        PayslipResult payslipResult = super.getOne(id);
        payslipResult.setStatus(PayslipResult.PayslipStatus.DEFAULT);
        payslipResult.setPayslipHeadSnapshot(null);
        payslipResult.getStaffPayslipList().forEach(staffPayslip -> {
            if (staffPayslip.getStatus() == PayslipResult.PayslipStatus.PUBLISH) {
                staffPayslip.setStatus(PayslipResult.PayslipStatus.DEFAULT);
            }
        });
        return super.createOrUpdate(payslipResult.getId(), payslipResult);
    }

    @Override
    public PayslipResult changePayslipConfig(Long id, Long payslipConfigId) {
        PayslipResult payslipResult = super.getOne(id);
        ValidationUtils.rejectIfEmpty(payslipConfigId, "", "没有找到资源");
        PayslipConfig payslipConfig = payslipConfigService.getOne(payslipConfigId);
        payslipResult.setPayslip(payslipConfig);
        return super.createOrUpdate(payslipResult.getId(), payslipResult);
    }

}
